// TODO: CODE REVIEW & CLEANUP!
//
// AppForm.cs
//
// The main application window.
//

using System;
using System.Configuration;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
using System.Xml;

namespace HomeUX.TouchScreen
{

public partial class AppForm : Form
{
    ///////////////////////////////////////////////////////////////////////////////////////////////
    // Constants
    //

    /// <summary>
    /// The name of this application, for use in message box captions.
    /// </summary>
    const string AppName = "HomeUX.TouchScreen";

    ///////////////////////////////////////////////////////////////////////////////////////////////
    // Private Fields
    //

    /// <summary>
    /// The value of the "IsFullScreenMode" application setting (see App.config).
    /// </summary>
    bool _isFullScreenMode;

    /// <summary>
    /// The value of the "WindowWidth" and "WindowHeight" application settings (see App.config).
    /// </summary>
    Size _windowSize;

    /// <summary>
    /// The value of the "ScreenDeviceName" application setting (see App.config).
    /// </summary>
    string _screenDeviceName;

    /// <summary>
    /// The value of the "AppUrl" application setting (see App.config).
    /// </summary>
    Uri _appUri;

    /// <summary>
    /// The popup pseudo-window that the the "Reload" button.
    /// </summary>
    UserControl _reloadPopup;

    /// <summary>
    /// Indicates that AppForm_Load has completed.
    /// </summary>
    bool _isLoaded;

    /// <summary>
    /// The number of times PollTimer_Tick has been called.
    /// </summary>
    int _pollCount;

    ///////////////////////////////////////////////////////////////////////////////////////////////
    // Public Methods
    //

    public AppForm()
    {
        // standard initialization
        InitializeComponent();
    }

    ///////////////////////////////////////////////////////////////////////////////////////////////
    // Event Handlers
    //

    void AppForm_Load(object sender, EventArgs e)
    {
        // read application settings from the .config file
        try
        {
            // read settings from the .config file
            _isFullScreenMode = GetBooleanAppSetting("IsFullScreenMode");
            _screenDeviceName = GetAppSetting("ScreenDeviceName");
            if (!_isFullScreenMode)
            {
                _windowSize = new Size(GetInt32AppSetting("WindowWidth"),
                    GetInt32AppSetting("WindowHeight"));
            }
            string appUrl = GetAppSetting("AppUrl");
            try
            {
                _appUri = new Uri(appUrl);
            }
            catch (FormatException ex)
            {
                throw new ApplicationException(String.Format(
                    "The \"AppUrl\" application setting \"{0}\" is incorrectly formatted: {1}",
                    appUrl, ex.Message));
            }

            // position the window
            CheckAppWindowPosition();

            // create <_reloadPopup>
            CreateReloadPopup();

            // navigate <WebBrowser> to the server HTML page
            TryToConnect();

            // update state
            _isLoaded = true;
        }
        catch (ApplicationException ex)
        {
            MessageBox.Show(ex.Message, AppName);
            Close();
        }
    }

    void reloadButton_Click(object sender, EventArgs e)
    {
        TryToConnect();
    }

    private void ExitMenuItem_Click(object sender, EventArgs e)
    {
        Close();
    }

    private void PollTimer_Tick(object sender, EventArgs e)
    {
        _reloadPopup.Visible = (WebBrowser.Document == null) ||
            !WebBrowser.Document.Title.Contains("HomeUX");

        // periodically ensure that we're still full-screen on the correct monitor (in case e.g.
        // it's a secondary monitor that went to sleep and pushed us off the monitor)
        if (++_pollCount % 50 == 0)
        {
            if (_isLoaded && _isFullScreenMode)
            {
                try
                {
                    CheckAppWindowPosition();
                }
                catch
                {
                    // ignore errors
                }
            }
        }
    }

    void CheckAppWindowPosition()
    {
        // set <screen> to the screen specified by <_screenDeviceName>, or the primary screen if
        // <_screenDeviceName> doesn't refer to an existing screen
        Screen screen = Screen.AllScreens.Where(s => s.DeviceName == _screenDeviceName)
            .FirstOrDefault();
        if (screen == null)
            screen = Screen.PrimaryScreen;

        if (_isFullScreenMode)
        {
            // move this application's window to <screen>
            Location = screen.Bounds.Location;
            Size = screen.Bounds.Size;

            // show the tray icon
            TrayIcon.Visible = true;
        }
        else
        {
            // configure the application for non-full-screen mode
            Size = _windowSize;
            Location = new Point(
                (screen.WorkingArea.Width - Size.Width) / 2 + screen.WorkingArea.Left,
                (screen.WorkingArea.Height - Size.Height) / 2 + screen.WorkingArea.Top);
            Padding = new Padding(5, 0, 5, 5);
            FormBorderStyle = FormBorderStyle.FixedSingle;
            ShowInTaskbar = true;
        }
    }

    ///////////////////////////////////////////////////////////////////////////////////////////////
    // Private Helper Methods
    //

    /// <summary>
    /// Creates <r>_reloadPopup</r>.
    /// </summary>
    ///
    void CreateReloadPopup()
    {
        // set <popupFont> to the default font to use
        var popupFont = new Font(FontFamily.GenericSansSerif, 13);

        // set <popupSize> to the outer dimensions of the popup
        var popupSize = new System.Drawing.Size(500, 200);

        // create <_reloadPopup>
        _reloadPopup = new UserControl()
        {
            Size = popupSize,
            Location = new Point((Size.Width - popupSize.Width) / 2,
                (Size.Height - popupSize.Height) / 2),
            BackColor = Color.LightPink,
            BorderStyle = BorderStyle.FixedSingle,
            Visible = true
        };

        // add a "Loading..." label to <_reloadPopup>
        _reloadPopup.Controls.Add(new Label()
        {
            Location = new Point(10, 10),
            Width = _reloadPopup.Width - 20,
            AutoEllipsis = true,
            Text = "Loading...",
            Font = popupFont
        });

        // add a "Loading from..." label to <_reloadPopup>
        _reloadPopup.Controls.Add(new Label()
        {
            Location = new Point(10, _reloadPopup.Height - 24),
            Width = _reloadPopup.Width - 20,
            AutoEllipsis = true,
            Text = String.Format("Loading from: {0}", _appUri)
        });

        // add a "Reload" button to <_reloadPopup>
        var reloadButtonSize = new System.Drawing.Size(100, 75);
        var reloadButton = new Button()
        {
            Size = reloadButtonSize,
            BackColor = Color.Silver,
            Location = new Point((_reloadPopup.Size.Width - reloadButtonSize.Width) / 2,
                (_reloadPopup.Size.Height - reloadButtonSize.Height) / 2),
            Text = "Reload",
            Font = popupFont
        };
        reloadButton.Click += reloadButton_Click;
        _reloadPopup.Controls.Add(reloadButton);

        // add <reloadPopup> to the main window
        Controls.Add(_reloadPopup);
        Controls.SetChildIndex(_reloadPopup, 0);
    }

    /// <summary>
    /// Navigates <r>WebBrowser</r> to <r>_appUri</r>.
    /// </summary>
    void TryToConnect()
    {
        WebBrowser.Url = _appUri;
    }

    /// <summary>
    /// Returns the string result of an application setting that's in the .config file.
    /// </summary>
    ///
    /// <param name="settingName">The name of the application setting.</param>
    ///
    /// <exception cref="ApplicationException">
    /// The application setting wasn't found.
    /// </exception>
    ///
    static string GetAppSetting(string settingName)
    {
        string value = ConfigurationManager.AppSettings[settingName];
        if (value == null)
        {
            throw new ApplicationException(String.Format(
                "Missing application setting \"{0}\" in the .config file", settingName));
        }
        return value;
    }

    /// <summary>
    /// Returns the <n>Int32</n> result of an application setting that's in the .config file.
    /// </summary>
    ///
    /// <param name="settingName">The name of the application setting.</param>
    ///
    /// <exception cref="ApplicationException">
    /// The application setting wasn't found, or isn't an <n>Int32</n> value.
    /// </exception>
    ///
    static Int32 GetInt32AppSetting(string settingName)
    {
        string stringValue = GetAppSetting(settingName);
        try
        {
            return XmlConvert.ToInt32(stringValue);
        }
        catch (Exception ex)
        {
            if ((ex is FormatException) || (ex is OverflowException))
            {
                throw new ApplicationException(String.Format(
                    "Expected AppSetting \"{0}\" to have an integer value; actual value is \"{1}\"",
                    settingName, stringValue));
            }
            else
                throw;
        }
    }

    /// <summary>
    /// Returns the <n>Boolean</n> result of an application setting that's in the .config file.
    /// </summary>
    ///
    /// <param name="settingName">The name of the application setting.</param>
    ///
    /// <exception cref="ApplicationException">
    /// The application setting wasn't found, or isn't a <n>Boolean</n> value.
    /// </exception>
    ///
    static Boolean GetBooleanAppSetting(string settingName)
    {
        string stringValue = GetAppSetting(settingName);
        try
        {
            return XmlConvert.ToBoolean(stringValue);
        }
        catch (FormatException)
        {
            throw new ApplicationException(String.Format(
                "Expected AppSetting \"{0}\" to have value \"true\" or \"false\"; actual value is \"{1}\"",
                settingName, stringValue));
        }
    }
}

}
